package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.xuecheng.api.content.model.dto.TeachplanDTO;
import com.xuecheng.api.content.model.dto.TeachplanMediaDTO;
import com.xuecheng.api.media.model.dto.MediaDTO;
import com.xuecheng.common.domain.code.CommonErrorCode;
import com.xuecheng.common.domain.response.RestResponse;
import com.xuecheng.common.enums.common.CommonEnum;
import com.xuecheng.common.enums.content.CourseAuditEnum;
import com.xuecheng.common.enums.content.CourseChargeEnum;
import com.xuecheng.common.enums.content.TeachPlanEnum;
import com.xuecheng.common.exception.ExceptionCast;
import com.xuecheng.content.agent.MediaApiAgent;
import com.xuecheng.content.common.constant.ContentErrorCode;
import com.xuecheng.content.convert.TeachplanConvert;
import com.xuecheng.content.convert.TeachplanMediaConvert;
import com.xuecheng.content.entity.CourseBase;
import com.xuecheng.content.entity.Teachplan;
import com.xuecheng.content.entity.TeachplanMedia;
import com.xuecheng.content.entity.ex.TeachplanNode;
import com.xuecheng.content.mapper.CourseBaseMapper;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.service.CourseBaseService;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 课程计划 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {

    @Autowired
    private CourseBaseService courseBaseService;

    /**
     * 1.判断关键数据
     * 课程基础信息的Id值
     * 公司id值
     * 2.查询课程计划所对应的课程是否存在
     * PS：课程计划要所属于一个课程下，课程计划是没有单独存在的意义
     * 判断是否是一家教学机构
     * 判断课程是否删除
     * 3.根据课程基础信息的id值查询课程的计划数据（List）
     * 4.通过java的递归来生成课程计划的树形结构
     * 5.将数据转为dto数据
     */
    public TeachplanDTO queryTreeNodesByCourseId(Long courseBaseId, Long companyId) {
        //1.判断关键数据
        //课程基础信息的Id值
        //公司id值
        if (ObjectUtils.isEmpty(courseBaseId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //2.查询课程计划所对应的课程是否存在
        verifyTeachplanCourseMsg(courseBaseId, companyId);
        //3.根据课程基础信息的id值查询课程的计划数据（List）
        TeachplanMapper baseMapper = getBaseMapper();
        List<TeachplanNode> teachplanNodes = baseMapper.selectTreeNodesByCourseBaseId(courseBaseId);
        //4.通过java的递归来生成课程计划的树形结构
        // 获得课程计划的一级内容
        TeachplanDTO teachplanDTO = null;
        if (!ObjectUtils.isEmpty(teachplanNodes)) {
            TeachplanNode rootNode = teachplanNodes.remove(0);
            generateTreeNode(rootNode, teachplanNodes);

            //5.将数据转为dto数据
            teachplanDTO = TeachplanConvert.INSTANCE.node2dto(rootNode);
        } else {
            teachplanDTO = new TeachplanDTO();
        }

        return teachplanDTO;
    }

    private void verifyTeachplanCourseMsg(Long courseBaseId, Long companyId) {
        //查询课程计划所对应的课程是否存在
        CourseBase courseBase = courseBaseService.getById(courseBaseId);
        if (ObjectUtils.isEmpty(courseBase)) {
            ExceptionCast.cast(ContentErrorCode.E_120013);
        }
        if (!ObjectUtils.isEmpty(companyId)) {
            //判断是否是一家教学机构
            if (!(ObjectUtils.nullSafeEquals(companyId, courseBase.getCompanyId()))) {
                ExceptionCast.cast(CommonErrorCode.E_100108);
            }
        }

        //判断课程是否删除
        if (CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBase.getStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120415);
        }
    }

    /*
     *  生成树形结构数据
     * */

    private void generateTreeNode(TeachplanNode prentNode, List<TeachplanNode> nodes) {
        //判断父节点集合数据是否为空
        if (ObjectUtils.isEmpty(prentNode.getChildrenNodes())) {
            prentNode.setChildrenNodes(new ArrayList<TeachplanNode>());
        }
        // 2.循环遍历集合数据生成树形结构
        for (TeachplanNode node : nodes) {

            if (ObjectUtils.nullSafeEquals(node.getParentid(), prentNode.getId())) {
                // 2.1 将子数据存放到父节点的集合数据中
                prentNode.getChildrenNodes().add(node);

                // 2.2 递归获得node的子数据
                if (!TeachPlanEnum.THIRD_LEVEL.equals(node.getGrade())) {
                    generateTreeNode(node, nodes);
                } else {
                    //如果课程计划为三级,查询是否有关联的媒资信息
                    LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(TeachplanMedia::getTeachplanId, node.getId());

                    TeachplanMedia teachplanMedia = teachplanMediaService.getOne(queryWrapper);
                    if (!ObjectUtils.isEmpty(teachplanMedia)) {
                        node.setTeachplanMedia(teachplanMedia);
                    }
                }
            }
        }
    }

    /**
     * 1.判断业务的具体操作
     * 如果数据有Id值==> 修改操作
     * 如果数据无Id值==> 新增操作
     * 2.通过数据的特点来完成业务的操作
     * 添加课程计划数据（无id值）
     * 修改课程计划数据（有id值）
     */
    @Transactional
    public TeachplanDTO createOrModifyTeachPlan(TeachplanDTO dto, Long companyId) {
        //1.判断业务的具体操作
        Long teachPlanId = dto.getTeachPlanId();
        TeachplanDTO resultDto = null;

        if (ObjectUtils.isEmpty(teachPlanId)) {
            //创建课程计划数据
            resultDto = createTeachplan(dto, companyId);

        } else {
            //修改课程计划数据
            resultDto = modifyTeachplan(dto, companyId);

        }
        return resultDto;
    }


    /*创建课程计划数据*/
    /*
     * 业务分析：
     *   1.事务传递（自己复习spring）
     *   2.判断关键数据
     *       前端页面无法观察
     *       后端数据库（字段不能为空）
     *         pname，parentId、grade、course_id
     *   3.判断课程计划所对应课程是否存在
     *     PS：课程计划要所属于一个课程下，课程计划是没有单独存在的意义
     *        判断是否是一家教学机构
     *        判断课程是否删除
     *  4.添加课程计划
     *    pname：前端传
     *    parentId：二级数据需要后端
     *             三级数据需要前端
     *    grade：由后端来生成
     *    course_id：前端传
     *  5.转换数据po=>dto
     *
     * */
    private TeachplanDTO createTeachplan(TeachplanDTO dto, Long companyId) {
        // 1.判断关键数据
        if (ObjectUtils.isEmpty(dto.getPname())) {
            ExceptionCast.cast(ContentErrorCode.E_120413);
        }
        if (ObjectUtils.isEmpty(dto.getCourseId())) {
            ExceptionCast.cast(ContentErrorCode.E_120404);
        }
        // 2.判断课程计划所对应课程是否存在
        verifyTeachplanCourseMsg(dto.getCourseId(), companyId);
        // 3.添加课程计划
        //dto-->po
        Teachplan teachplan = TeachplanConvert.INSTANCE.dto2entity(dto);
        // 获得父节点的id值==>parentId  二级数据需要后端  三级数据需要前端
        Teachplan parentNode = getParentNode(dto, companyId);
        // grade：由后端来生成
        // 父节点的grade值+1
        Integer parentGrade = parentNode.getGrade();
        teachplan.setGrade(parentGrade + 1);
        // orderby:获得父节点的子数据个数+1
        LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Teachplan::getParentid, parentNode.getId());
        int count = this.count(queryWrapper);
        teachplan.setOrderby(count + 1);
        // 保存课程计划数据需要关联父节点数据
        teachplan.setParentid(parentNode.getId());

        // 如果是添加操作，数据特点为id为空
        teachplan.setId(null);

        boolean result = this.save(teachplan);
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }
        // 4.转换数据po=>dto
        TeachplanDTO resultDto = TeachplanConvert.INSTANCE.entity2dto(teachplan);

        return resultDto;
    }

    /*修改课程计划数据*/
    /*
    * 1.判断关键数据
    *   pname、companyId、判断课程计划数据是否存在
      2.判断课程计划所对应课程是否存在
    *     PS：课程计划要所属于一个课程下，课程计划是没有单独存在的意义
     *        判断是否是一家教学机构
     *        判断课程是否删除
     *3.修改课程计划数据
     *   parentid、grade、orderBy
    * 4.将修改后的数据转为dto返回前端
    *
    * */

    private TeachplanDTO modifyTeachplan(TeachplanDTO dto, Long companyId) {
        //1.判断关键数据
        if (ObjectUtils.isEmpty(dto.getPname()) || ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //判断课程计划数据是否存在
        Teachplan teachplan = this.getById(dto.getTeachPlanId());
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        //2.判断课程计划所对应课程是否存在
        verifyTeachplanCourseMsg(dto.getCourseId(), companyId);
        //3.修改课程计划数据
        //dto-->po
        Teachplan po = TeachplanConvert.INSTANCE.dto2entity(dto);
        // parentid、grade、orderBy 不让前端进行修改，通过数据库的数据进行覆盖
        po.setParentid(teachplan.getParentid());
        po.setGrade(teachplan.getGrade());
        po.setOrderby(teachplan.getOrderby());
        boolean result = this.updateById(po);
        if (!result) {
            ExceptionCast.cast(ContentErrorCode.E_120407);
        }
        //4.将修改后的数据转为dto返回前端
        TeachplanDTO resultDto = TeachplanConvert.INSTANCE.entity2dto(po);
        return resultDto;
    }
    /* 获得课程计划的父节点数据 */
    /*
    * 1.课程计划的一级（根级）

        - 数据来源：课程基础信息（CourseBase）
        - 创建时机：在第一次添加二级数据时自动生成
        - 注意事项：一级父级（根级）父级 ID 值为 0

    2.课程计划的二级和三级节点

        - 数据来源：教育机构人员在页面填写的数据
        - 创建时机：教育机构人员提交课程计划数据
        - 注意事项：添加时要校验父级菜单是否存在
          - 二级在添加时
            - 父级不存在（一级）：自动创建并获得获得父级Id值
            - 父级存在（一级）：获得父级Id值
          - 三级在添加时
            - 父级不存在（二级）：抛出异常
            - 父级存在（二级）：获得父级Id值
    *
    * */

    private Teachplan getParentNode(TeachplanDTO dto, Long companyId) {
        // 1.判断课程计划的等级：是二级还是三级
        // 如果parentid是存在的：三级课程计划
        // 如果parentid是不存在的：二级课程计划
        if (ObjectUtils.isEmpty(dto.getParentid())) {
            // 二级课程计划父类数据
            // 根据课程id值查询
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getCourseId, dto.getCourseId());
            // 根据parentid=0
            queryWrapper.eq(Teachplan::getParentid, TeachPlanEnum.FIRST_PARENTID_FLAG);
            Teachplan rootNode = this.getOne(queryWrapper);
            // - 父级不存在（一级）：自动创建并获得获得父级Id值
            if (ObjectUtils.isEmpty(rootNode)) {
                // 根据courseid查询课程基础信息
                CourseBase courseBase = courseBaseService.getById(dto.getCourseId());
                // 自动创建一级课程计划
                rootNode = new TeachplanNode();
                rootNode.setParentid(new Long(TeachPlanEnum.FIRST_PARENTID_FLAG));
                rootNode.setPname(courseBase.getName());
                rootNode.setGrade(TeachPlanEnum.FIRST_LEVEL);
                rootNode.setDescription(courseBase.getDescription());
                rootNode.setOrderby(TeachPlanEnum.FIRST_LEVEL);
                rootNode.setCourseId(dto.getCourseId());
                boolean result = this.save(rootNode);
                if (!result) {
                    ExceptionCast.cast(ContentErrorCode.E_120416);
                }
            }
            // - 父级存在（一级）：获得父级Id值
            return rootNode;
        } else {
            // 三级课程计划父类数据
            Teachplan secNode = this.getById(dto.getParentid());
            if (ObjectUtils.isEmpty(secNode)) {
                //- 父级不存在（二级）：抛出异常
                ExceptionCast.cast(ContentErrorCode.E_120408);
            }
            // - 父级存在（二级）：获得父级Id值
            return secNode;
        }
    }

    @Autowired
    private TeachplanMediaService mediaService;

    /**
     * 1.判断关键数据的合法性
     * 1.2 判断课程基本信息是否存在
     * 1.3 判断公司 Id是否和课程中的公司Id一致
     * 2. 根据课程计划等级进行业务判断
     * 判断二级课程计划是否有子级课程计划信息
     * 判断三级课程计划是否关联课程媒资信息
     * 4.根据Id删除课程计划
     * removeById(teachPlanId)
     */
    @Transactional
    public void removeTeachPlan(Long teachPlanId, Long companyId) {

        //1.判断关键数据的合法性
        Teachplan teachplan = this.getById(teachPlanId);
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        //1.2 判断课程基本信息是否存在
        verifyTeachplanCourseMsg(teachplan.getCourseId(), companyId);
        //2. 根据课程计划等级进行业务判断
        if (teachplan.getGrade().equals(TeachPlanEnum.SECEND_LEVEL)) {
            //判断二级课程计划是否有子级课程计划信息
            LambdaQueryWrapper<Teachplan> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Teachplan::getParentid, teachPlanId);
            int count = this.count(queryWrapper);
            if (count > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120409);
            }
            //根据Id删除课程计划
            boolean result = this.removeById(teachPlanId);
            if (!result) {
                ExceptionCast.cast(ContentErrorCode.E_120417);
            }
        } else {
            //判断三级课程计划是否关联课程媒资信息
            LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TeachplanMedia::getTeachplanId, teachPlanId);
            int count = mediaService.count(queryWrapper);
            if (count > 0) {
                ExceptionCast.cast(ContentErrorCode.E_120414);
            }
            //根据Id删除课程计划
            boolean result = this.removeById(teachPlanId);
            if (!result) {
                ExceptionCast.cast(ContentErrorCode.E_120417);
            }
        }
    }

    @Autowired
    private TeachplanMediaService teachplanMediaService;

    @Autowired
    private MediaApiAgent mediaApiAgent;

    /**
     * 1.判断关键数据合法性
     * teachplanId  mediaId  companyId
     * 2.判断业务数据
     * 判断媒资信息
     * 判断媒资信息是否存在
     * 判断是否是同一家机构
     * 判断课程计划信息
     * 判断课程计划是否存在
     * 判断是否同一家机构
     * 判断课程的审核状态
     * 判断课程是否删除
     * 判断课程计划等级(只有三级才可以绑定媒资信息)
     * 3.课程计划和媒资信息的绑定
     * 判断数据绑定操作(添加 / 修改)
     * 判断课程计划媒资信息是否存在(teachplanId  mediaId)
     * 4.查询最新数据转换为dto返回前端
     */
    @Transactional
    public TeachplanMediaDTO associateMedia(TeachplanMediaDTO dto, Long companyId) {
        //1.判断关键数据合法性
        if (ObjectUtils.isEmpty(dto.getTeachplanId()) ||
                ObjectUtils.isEmpty(dto.getMediaId()) ||
                ObjectUtils.isEmpty(companyId)) {
            ExceptionCast.cast(CommonErrorCode.E_100101);
        }
        //2.判断业务数据
        //判断媒资信息
        RestResponse<MediaDTO> response = mediaApiAgent.getMediaById4s(dto.getMediaId());
        //判断媒资信息是否存在
        if (!response.isSuccessful()) {
            ExceptionCast.castWithCodeAndDesc(response.getCode(), response.getMsg());
        }
        //判断是否是同一家机构
        MediaDTO mediaDTO = response.getResult();
        if (!ObjectUtils.nullSafeEquals(companyId, mediaDTO.getCompanyId())) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }
        //判断课程计划信息
        Teachplan teachplan = this.getById(dto.getTeachplanId());
        //判断课程计划是否存在
        if (ObjectUtils.isEmpty(teachplan)) {
            ExceptionCast.cast(ContentErrorCode.E_120402);
        }
        //判断是否同一家机构
        CourseBase courseBase = courseBaseService.getById(teachplan.getCourseId());
        if (!ObjectUtils.nullSafeEquals(companyId, courseBase.getCompanyId())) {
            ExceptionCast.cast(CommonErrorCode.E_100108);
        }
        //判断课程的审核状态
        if (CourseAuditEnum.AUDIT_COMMIT_STATUS.getCode().equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_PUBLISHED_STATUS.getCode().equals(courseBase.getAuditStatus()) ||
                CourseAuditEnum.AUDIT_PASTED_STATUS.getCode().equals(courseBase.getAuditStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120014);
        }
        //判断课程是否删除
        if (CommonEnum.DELETE_FLAG.getCodeInt().equals(courseBase.getStatus())) {
            ExceptionCast.cast(ContentErrorCode.E_120019);
        }
        //判断课程计划等级(只有三级才可以绑定媒资信息)
        if (TeachPlanEnum.THIRD_LEVEL != teachplan.getGrade()) {
            ExceptionCast.cast(ContentErrorCode.E_120410);
        }
        //3.课程计划和媒资信息的绑定
        //判断数据绑定操作(添加 / 修改)
        //判断课程计划媒资信息是否存在(teachplanId  mediaId)
        // PS:一个课程计划只能绑定一个媒资信息
        //根据teachplanId查询课程媒资信息
        LambdaQueryWrapper<TeachplanMedia> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TeachplanMedia::getTeachplanId, dto.getTeachplanId());
        TeachplanMedia teachplanMedia = teachplanMediaService.getOne(queryWrapper);
        //根据mediaId查询课程媒资信息
        LambdaQueryWrapper<TeachplanMedia> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(TeachplanMedia::getMediaId, dto.getMediaId());
        TeachplanMedia teachplanMedia1 = teachplanMediaService.getOne(queryWrapper1);

        if (ObjectUtils.isEmpty(teachplanMedia) && ObjectUtils.isEmpty(teachplanMedia1)) {
            //添加
            teachplanMedia = new TeachplanMedia();
            teachplanMedia.setTeachplanId(dto.getTeachplanId());
            teachplanMedia.setMediaId(dto.getMediaId());
            teachplanMedia.setCourseId(courseBase.getId());
            teachplanMedia.setMediaFilename(mediaDTO.getFilename());

            boolean result = teachplanMediaService.save(teachplanMedia);
            if (!result) {
                ExceptionCast.cast(ContentErrorCode.E_120411);
            }
        }
        if ((!ObjectUtils.isEmpty(teachplanMedia)) && (!ObjectUtils.isEmpty(teachplanMedia1))) {

            if (!ObjectUtils.nullSafeEquals(teachplanMedia.getMediaId(), teachplanMedia1.getMediaId())) {
                ExceptionCast.cast(ContentErrorCode.E_120418);
            }
        }
        if ((!ObjectUtils.isEmpty(teachplanMedia)) && ObjectUtils.isEmpty(teachplanMedia1)) {

            //修改
            LambdaUpdateWrapper<TeachplanMedia> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(TeachplanMedia::getMediaId, dto.getMediaId());
            updateWrapper.set(TeachplanMedia::getMediaFilename, mediaDTO.getFilename());
            updateWrapper.eq(TeachplanMedia::getId, teachplanMedia.getId());

            boolean update = teachplanMediaService.update(updateWrapper);
            if (!update) {
                ExceptionCast.cast(ContentErrorCode.E_120411);
            }
        }
        if (ObjectUtils.isEmpty(teachplanMedia) && (!ObjectUtils.isEmpty(teachplanMedia1))) {
            ExceptionCast.cast(ContentErrorCode.E_120418);
        }
       /* if (ObjectUtils.isEmpty(teachplanMedia)) {
            //添加
            teachplanMedia = new TeachplanMedia();
            teachplanMedia.setTeachplanId(dto.getTeachplanId());
            teachplanMedia.setMediaId(dto.getMediaId());
            teachplanMedia.setCourseId(courseBase.getId());
            teachplanMedia.setMediaFilename(mediaDTO.getFilename());

            boolean result = teachplanMediaService.save(teachplanMedia);
            if (!result) {
                ExceptionCast.cast(ContentErrorCode.E_120411);
            }
        } else {
            //修改
            LambdaUpdateWrapper<TeachplanMedia> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(TeachplanMedia::getMediaId, dto.getMediaId());
            updateWrapper.set(TeachplanMedia::getMediaFilename, mediaDTO.getFilename());
            updateWrapper.eq(TeachplanMedia::getId, teachplanMedia.getId());

            boolean update = teachplanMediaService.update(updateWrapper);
            if (!update) {
                ExceptionCast.cast(ContentErrorCode.E_120411);
            }
        }*/
        //4.查询最新数据转换为dto返回前端
        TeachplanMedia po = teachplanMediaService.getById(teachplanMedia.getId());


        //po-->dto
        TeachplanMediaDTO resultDto = TeachplanMediaConvert.INSTANCE.entity2dto(po);
        return resultDto;
    }
}
